﻿namespace TuneMultiCheckBox.Logic.TuneMultiCheckBox
{
  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.Linq;
  using System.Text;
  using System.Xml;
  using System.Xml.Linq;
  using Entities;
  using Microsoft.Xrm.Sdk;

  public class Configuration
  {
    public const string ConfigFileWebResourceName = @"tunecrm_TuneCrmTools/xml/TuneMultiCheckBox/config.xml";

    public Configuration(PluginBase<Microsoft.Xrm.Sdk.Entity>.LocalPluginContext localContext)
    {
      var context = localContext.OrganizationContext;
      var configDocument = GetConfigFile(context);
      if (configDocument == null || configDocument.Root == null)
      {
        var message = string.Format("tuneMultiCheckBox configuration file / webresource ({0}) contains incorrect content or invalid XML.", ConfigFileWebResourceName);
        throw BuildConfigException(message);
      }

      try
      {
        var crmEntities = configDocument.Root.Element("crmEntities").Descendants("crmEntity");
        this.Entities = new List<Entity>();
        foreach (var crmEntity in crmEntities)
        {
          var entity = new Entity
          {
            LogicalName = ((string)crmEntity.Attribute("logicalName")).ToLowerInvariant(),
            CheckboxSets = new List<CheckboxSet>()
          };

          var controls = crmEntity.Descendants("checkboxSet");
          foreach (var control in controls)
          {
            var checkboxSet = new CheckboxSet
            {
              Name = (string)control.Attribute("name"),
              RelationshipName = (string)control.Attribute("relationshipName"),
              FakeSavingField = (string)control.Attribute("fakeSavingField"),
            };

            entity.CheckboxSets.Add(checkboxSet);
          }

          this.Entities.Add(entity);
        }
      }
      catch (Exception exception)
      {
        var message = string.Format("tuneMultiCheckBox configuration file / webresource ({0}) contains incorrect XML.", ConfigFileWebResourceName);
        throw BuildConfigException(message, exception);
      }
    }

    public IList<Entity> Entities { get; private set; }

    public static InvalidPluginExecutionException BuildConfigException(string message, Exception innerException = null)
    {
      const string ErrorNote =
        "tuneMultiCheckBox tool has incorrect configuration. \n\r" +
        "Please check and correct settings in the tuneMultiCheckBox configuration file.\n\r" +
        "Please follow the documentation on http://tunemulticheckbox.codeplex.com/documentation to configure the tuneMultiCheckBox settings properly.\n\r" +
        "\n\rError Details:\n\r";

      message = ErrorNote + message;
      if (innerException != null)
      {
        message = message + "\n\rInner Error:\n\r" + innerException.Message;
      }

      return new InvalidPluginExecutionException(message);
    }

    private static XDocument GetConfigFile(CrmContext context)
    {
      var content = context.WebResourceSet
                           .Where(r => r.Name == ConfigFileWebResourceName)
                           .Select(r => r.Content)
                           .FirstOrDefault();
      if (content == null)
      {
        var message = string.Format("tuneMultiCheckBox configuration file / webresource ({0}) is not found.", ConfigFileWebResourceName);
        throw BuildConfigException(message);
      }

      try
      {
        XDocument result;

        var binary = Convert.FromBase64String(content);

        using (var xmlStream = new MemoryStream(binary))
        {
          using (var xmlReader = new XmlTextReader(xmlStream))
          {
            result = XDocument.Load(xmlReader, LoadOptions.None);
          }
        }

        return result;
      }
      catch (Exception exception)
      {
        var message = string.Format("tuneMultiCheckBox configuration file / webresource ({0}) contains incorrect content or invalid XML.", ConfigFileWebResourceName);
        throw BuildConfigException(message, exception);
      }
    }

    public class Entity
    {
      public string LogicalName { get; set; }

      public IList<CheckboxSet> CheckboxSets { get; set; }
    }

    public class CheckboxSet
    {
      public string Name { get; set; }

      public string FakeSavingField { get; set; }

      public string RelationshipName { get; set; }
    }
  }
}
